#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>

#define MYNAME  "myled"
#define RCC_MP_AHB4ENSETR_PHY_ADDR  0x50000A28
#define GPIOE_MODER_PHY_ADDR    0x50006000
#define GPIOE_OTYPER_PHY_ADDR   0x50006004
#define GPIOE_OSPEEDR_PHY_ADDR  0x50006008
#define GPIOE_PUPDR_PHY_ADDR    0x5000600C
#define GPIOE_ODR_PHY_ADDR      0x50006014

//接收返回值
volatile unsigned int * rcc_virl_addr;
volatile unsigned int * gpioe_moder_virl_addr;
volatile unsigned int * gpioe_otyper_virl_addr;
volatile unsigned int * gpioe_ospeedr_virl_addr;
volatile unsigned int * gpioe_pupdr_virl_addr;
volatile unsigned int * gpioe_odr_virl_addr;
int major;
char kbuf[128] = {0};
int myled_open (struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__,__LINE__);
    return 0;
}
ssize_t myled_read (struct file *file, char __user *ubuf, size_t size, loff_t *offset)
{   
    int ret;
    printk("%s:%s:%d\n", __FILE__, __func__,__LINE__);
    // 校准拷贝数据的大小
    if (size > sizeof(kbuf)) size = sizeof(kbuf);
    // 将内核空间的数据拷贝到用户空间
    ret = copy_to_user(ubuf,kbuf,size);
    if (ret) {
        printk("kernel space copy data to user space failed!\n");
        return -EINVAL;
    }
    // 成功返回拷贝成功的字节的个数
    return size;
}
/*
    需要指定一个led灯控制的协议
    kbuf[0] = 1 : 表示控制LED1灯
    kbuf[0] = 2 : 表示控制LED2灯
    kbuf[0] = 3 : 表示控制LED3灯
    kbuf[1] = 0 ：表示熄灭LED灯
    kbuf[1] = 1 ：表示点亮LED灯
*/
ssize_t myled_write (struct file *file, const char __user *ubuf, size_t size, loff_t *offset)
{
    int ret;
    printk("%s:%s:%d\n", __FILE__, __func__,__LINE__);
     // 校准拷贝数据的大小
    if (size > sizeof(kbuf)) size = sizeof(kbuf);
    // 将用户空间的数据拷贝到内核空间
    ret = copy_from_user(kbuf,ubuf,size);
    if (ret) {
        printk("user space copy data to kernel space failed!\n");
        return -EINVAL;
    }
    switch (kbuf[0]) {
    case 1:
        if (kbuf[1] == 1) 
            *gpioe_odr_virl_addr |= (0x1 << 10);
        else
            *gpioe_odr_virl_addr &= (~(0x1 << 10));
        break;
    case 2:
        break;
    case 3:
        break;
    default:
        break;
    }

    return size;
}
int myled_close (struct inode *inode, struct file *file)
{
    printk("%s:%s:%d\n", __FILE__, __func__,__LINE__);
    return 0;
}

struct file_operations fops = {
    .open = myled_open,
    .read = myled_read,
    .write = myled_write,
    .release = myled_close,
};
// 完成led灯的初始化，
int led_init(void)
{
    int ret = 0;
    // 完成物理地址到虚拟地址的映射
    rcc_virl_addr = ioremap(RCC_MP_AHB4ENSETR_PHY_ADDR, 4);
    if (rcc_virl_addr == NULL) 
    {
        printk("memory map failed!\n");
        ret = -ENOMEM;
    }
    gpioe_moder_virl_addr = ioremap(GPIOE_MODER_PHY_ADDR, 4);
    if (gpioe_moder_virl_addr == NULL) 
    {
        printk("memory map failed!\n");
        ret = -ENOMEM;
    }
    gpioe_otyper_virl_addr = ioremap(GPIOE_OTYPER_PHY_ADDR, 4);
    if (gpioe_otyper_virl_addr == NULL) 
    {
        printk("memory map failed!\n");
        ret = -ENOMEM;
    }
    gpioe_ospeedr_virl_addr = ioremap(GPIOE_OSPEEDR_PHY_ADDR, 4);
    if (gpioe_ospeedr_virl_addr == NULL) 
    {
        printk("memory map failed!\n");
        ret = -ENOMEM;
    }
    gpioe_pupdr_virl_addr = ioremap(GPIOE_PUPDR_PHY_ADDR, 4);
    if (gpioe_pupdr_virl_addr == NULL) 
    {
        printk("memory map failed!\n");
        ret = -ENOMEM;
    }
    gpioe_odr_virl_addr = ioremap(GPIOE_ODR_PHY_ADDR, 4);
    if (gpioe_odr_virl_addr == NULL) 
    {
        printk("memory map failed!\n");
        ret = -ENOMEM;
    }
    // 使能gpioe外设的时钟
    *rcc_virl_addr = (0x1 << 4);
    // 初始化GPIO引脚为输出，推挽输出，低俗模式，禁止上拉下拉，默认输出低电平
    *gpioe_moder_virl_addr &= (~(0x3 << 20));
    *gpioe_moder_virl_addr |= (0x1 << 20);
    *gpioe_otyper_virl_addr &= (~(0x1 << 10));
    *gpioe_ospeedr_virl_addr &= (~(0x3 << 20));
    *gpioe_pupdr_virl_addr &= (~(0x3 << 20));
    *gpioe_odr_virl_addr &= (~(0x1 << 10));
    
    return ret;
}
// 取消led灯的初始化
void led_delInit(void)
{
    iounmap(gpioe_odr_virl_addr);
    iounmap(gpioe_pupdr_virl_addr);
    iounmap(gpioe_ospeedr_virl_addr);
    iounmap(gpioe_otyper_virl_addr);
    iounmap(gpioe_moder_virl_addr);
    iounmap(rcc_virl_addr);
}
static int __init myled_init(void)
{
    int ret;
    // 注册字符设备
    major = register_chrdev(0, MYNAME, &fops);
    if (major < 0) {
        printk("register char device failed!\n");
        return -EIO;
    }

    ret = led_init();
    if (ret < 0) {
        printk("led init failed!\n");
        return ret;
    }
    return 0;
}

static void __exit myled_exit(void)
{
    led_delInit();
    unregister_chrdev(major, MYNAME);
}
module_init(myled_init);
module_exit(myled_exit);

MODULE_LICENSE("GPL");